const getLotteryCookieRegex = /^https?:\/\/m\.client\.10010\.com\/dailylottery\/static\/(integral|doubleball)\/firstpage/;
const unicomCookieKey = "unicom_user_cookie";
const mobileKey = "unicom_mobile";
const encryptMobileKey = "unicom_encrypt_mobile";
const cityCodeKey = "city_code";
const scriptName = "中国联通";

let magicJS = MagicJS(scriptName, "DEBUG");
magicJS.unifiedPushUrl = magicJS.read("unicom_unified_push_url") || magicJS.read("magicjs_unified_push_url");

// 用户登录
function UserLogin(cookie, encryptMobile) {
    // 联通App签到
    return new Promise((resolve) => {
        if (cookie) {
            let options = {
                url: "http://m.client.10010.com/dailylottery/static/textdl/userLogin?flag=1",
                headers: {
                    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
                    "Accept-Encoding": "gzip, deflate",
                    "Accept-Language": "zh-cn",
                    "Connection": "close",
                    "Cookie": cookie,
                    "Host": "m.client.10010.com",
                    "Upgrade-Insecure-Requests": "1",
                    "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 unicom{version:iphone_c@6.0201}{systemVersion:dis}",
                },
            };
            magicJS.get(options, (err, resp, data) => {
                if (err) {
                    magicJS.logInfo("用户登录失败，http请求异常：" + err);
                    resolve([false, "用户登录失败"]);
                } else {
                    if (data.indexOf(encryptMobile) >= 0) {
                        magicJS.logInfo("用户登录成功");
                        resolve([true, "用户登录成功"]);
                    } else if (data.indexOf("请稍后重试") >= 0) {
                        magicJS.logInfo("用户登录失败");
                        resolve([false, "用户登录失败"]);
                    } else {
                        magicJS.logInfo("用户登录失败，接口响应不合法：" + data);
                        resolve([false, "用户登录失败"]);
                    }
                }
            });
        } else {
            resolve([false, "请先获取token再登录"]);
            magicJS.logInfo("请先获取cookie再刷新token");
        }
    });
}

// 联通App签到
function AppSignin(cookie) {
    return new Promise((resolve, reject) => {
        let options = {
            url: "https://act.10010.com/SigninApp/signin/daySign?vesion=0.5630763707346611",
            headers: {
                "Accept": "application/json, text/plain, */*",
                "Accept-Encoding": "gzip, deflate, br",
                "Accept-Language": "zh-cn",
                "Connection": "keep-alive",
                "Cookie": cookie,
                "Host": "act.10010.com",
                "Origin": "https://img.client.10010.com",
                "Referer": "https://img.client.10010.com/SigininApp/index.html",
                "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 unicom{version:iphone_c@7.0402}{systemVersion:dis}{yw_code:}",
                "savedata": "false",
            },
            body: "",
        };
        magicJS.post(options, (err, resp, data) => {
            if (err) {
                magicJS.logError("签到失败，http请求异常：" + err);
                magicJS.notify(scriptName, "", "❌签到失败，http请求异常！！");
                reject("签到失败");
            } else {
                let obj = {};
                try {
                    obj = JSON.parse(data);
                    if (obj["status"] === "0000") {
                        magicJS.logInfo("签到成功");
                        resolve([true, "签到成功", obj.data.prizeCount, obj.data.growValue, obj.data.flowerCount]);
                    } else if (obj["status"] === "0001") {
                        magicJS.logWarning("尚未登录");
                        resolve([false, "尚未登录", null, null, null]);
                    } else if (obj["status"] === "0002") {
                        magicJS.logWarning("重复签到");
                        resolve([true, "重复签到", null, null, null]);
                    } else {
                        magicJS.logWarning("签到异常，接口返回数据不合法。" + data);
                        reject("签到异常");
                    }
                } catch (err) {
                    magicJS.logError("签到异常，代码执行错误：" + err);
                    reject("执行错误");
                }
            }
        });
    });
}

// 签到积分翻倍
function DoubleAdPlaying(cookie, mobile) {
    let options = {
        url: "https://act.10010.com/SigninApp/signin/bannerAdPlayingLogo",
        headers: {
            "Accept": "application/json, text/plain, */*",
            "Accept-Encoding": "gzip, deflate, br",
            "Accept-Language": "zh-cn",
            "Connection": "keep-alive",
            "Content-Type": "application/x-www-form-urlencoded",
            "Cookie": cookie,
            "Host": "act.10010.com",
            "Origin": "https://img.client.10010.com",
            "Referer": `https://img.client.10010.com/SigininApp/index.html?version=iphone_c@8.0200&desmobile=${mobile}`,
            "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 unicom{version:iphone_c@8.0200}{systemVersion:dis}{yw_code:}"
        },
        body: {
            orderId: "24F051D47389A008A1161E17F92438EC",
            imei: "455baba549deb814f6c75395fbe2403f855aa6dbedb50bc2acbff0ef4aab96f9"
        },
    };
    return new Promise((resolve, reject) => {
        magicJS.post(options, (err, resp, data) => {
            if (err) {
                magicJS.logError("签到失败，http请求异常：" + err);
                reject("积分翻倍失败");
            } else {
                let obj = {};
                try {
                    magicJS.logDebug(`积分翻倍，接口响应：${data}`);
                    obj = JSON.parse(data);
                    if (obj["status"] === "0000" && obj.data.returnStr.indexOf("失败") >= 0) {
                        magicJS.logWarning(`积分翻倍失败：${obj.data.returnStr}`);
                        reject("积分翻倍失败");
                    } else if (obj["status"] === "0000") {
                        magicJS.logInfo("积分翻倍成功");
                        resolve(obj.data.prizeCount);
                    } else if (obj["status"] === "0010") {
                        magicJS.logWarning("积分重复翻倍");
                        resolve(0);
                    } else {
                        magicJS.logWarning(`积分翻倍失败，接口返回数据不合法：\n${data}`);
                        reject("积分翻倍失败");
                    }
                } catch (err) {
                    magicJS.logError("积分翻倍失败" + err);
                    reject("积分翻倍失败");
                }
            }
        });
    });
}

// 获取连续签到天数
function GetContinueCount(cookie) {
    return new Promise((resolve, reject) => {
        let options = {
            url: "https://act.10010.com/SigninApp/signin/getContinuCount?vesion=0.35425159102265746",
            headers: {
                "Accept": "application/json, text/javascript, */*; q=0.01",
                "Accept-Encoding": "gzip, deflate, br",
                "Accept-Language": "zh-cn",
                "Connection": "keep-alive",
                "Cookie": cookie,
                "Host": "act.10010.com",
                "Origin": "https://act.10010.com",
                "Referer": "https://act.10010.com/SigninApp/signin/querySigninActivity.htm?version=iphone_c@6.0201",
                "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
                "X-Requested-With": "XMLHttpRequest",
                "savedata": "false",
            },
            body: "",
        };
        magicJS.post(options, (err, resp, data) => {
            if (err) {
                ``;
                magicJS.logError("获取连续签到次数失败，http请求异常：" + err);
                reject("?");
            } else {
                if (data) {
                    let number = "?";
                    if (/^\d+$/.test(data)) {
                        number = data;
                    } else {
                        magicJS.logWarning("获取连续签到次数失败，接口响应不合法。" + data);
                    }
                    resolve(number);
                } else {
                    magicJS.logWarning("获取连续签到次数异常，没有获取到响应体。");
                    reject("?");
                }
            }
        });
    });
}

// 获取用户信息
function GetUserInfo(cookie, mobile) {
    let options = {
        url: `https://m.client.10010.com/mobileService/home/queryUserInfoSeven.htm?version=iphone_c@7.0402&desmobiel=${mobile}&showType=3`,
        headers: {
            "Accept": "*/*",
            "Accept-Encoding": "gzip, deflate, br",
            "Accept-Language": "zh-cn",
            "Connection": "close",
            "Cookie": cookie,
            "Host": "m.client.10010.com",
            "User-Agent": "ChinaUnicom4.x/240 CFNetwork/1121.2.2 Darwin/19.3.0",
        },
    };
    return new Promise((resolve, reject) => {
        magicJS.get(options, (err, resp, data) => {
            if (err) {
                magicJS.logError("获取用户信息失败，http请求异常：" + err);
                reject({});
            } else {
                let result = {};
                try {
                    let obj = JSON.parse(data);
                    if (obj.hasOwnProperty("data") && obj["data"].hasOwnProperty("dataList")) {
                        obj["data"]["dataList"].forEach((element) => {
                            if ("flow,fee,voice,point".indexOf(element["type"]) >= 0) {
                                if (element["number"] !== "-") {
                                    result[element["type"]] = `${element["remainTitle"]}${element["number"]}${element["unit"]}`;
                                } else {
                                    magicJS.logWarning("获取用户信息异常：" + data);
                                    reject("获取用户信息异常");
                                }
                            }
                        });
                        magicJS.logInfo("获取用户信息：" + JSON.stringify(result));
                        resolve(result);
                    } else {
                        magicJS.logWarning("获取用户信息异常，接口响应不合法：" + data);
                        reject("获取用户信息接口响应异常");
                    }
                } catch (err) {
                    magicJS.logError(`获取用户信息失败，代码执行异常：${err}，接口返回：${data}`);
                    reject("获取用户信息执行异常");
                }
            }
        });
    });
}

// 美团外卖优惠券
function GetMeituanCoupon(cookie) {
    let options = {
        url: "https://m.client.10010.com/welfare-mall-front/mobile/api/bj2402/v1?reqdata=%7B%22saleTypes%22%3A%22TY%22%2C%22amount%22%3A0%2C%22goodsId%22%3A%228a29ac8a72be05a70172c067722600b8%22%2C%22payWay%22%3A%22%22%2C%22imei%22%3A%22%22%2C%22proFlag%22%3A%22%22%2C%22points%22%3A0%2C%22scene%22%3A%22%22%2C%22promoterCode%22%3A%22%22%7D",
        headers: {
            "Accept": "application/json, text/plain, */*",
            "Accept-Encoding": "gzip, deflate, br",
            "Accept-Language": "zh-cn",
            "Connection": "keep-alive",
            "Cookie": cookie,
            "Host": "m.client.10010.com",
            "Origin": "https://img.client.10010.com",
            "Referer": "https://img.client.10010.com/superFriday/",
            "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 unicom{version:iphone_c@8.0004}{systemVersion:dis}{yw_code:}",
        },
    };
    return new Promise((resolve, reject) => {
        magicJS.get(options, (err, resp, data) => {
            if (err) {
                magicJS.logError("领取美团外卖优惠券异常，http请求异常：" + err);
                reject("美团外卖优惠券:请求异常");
            } else {
                let obj = {};
                try {
                    magicJS.logDebug(`领取美团外卖优惠券，接口返回：\n${data}`);
                    obj = JSON.parse(data);
                    if (obj.hasOwnProperty("code")) {
                        if (obj["code"] === "0" && obj["msg"] === "下单成功") {
                            magicJS.logInfo("领取美团外卖优惠券，领取成功");
                            resolve("美团外卖优惠券：领取成功");
                        } else if (obj["code"] === "1") {
                            magicJS.logWarning("领取美团外卖优惠券，达到领取上限");
                            resolve("美团外卖优惠券：达到领取上限");
                        } else if (obj["code"] === "200" && obj["msg"].indexOf("太火爆") >= 0) {
                            magicJS.logWarning("领取美团外卖优惠券，活动太火爆");
                            resolve("美团外卖优惠券：活动太火爆领取失败");
                        } else {
                            magicJS.logWarning("领取美团外卖优惠券，接口响应不合法：" + data);
                            reject("接口响应不合法");
                        }
                    } else {
                        magicJS.logWarning("领取美团外卖优惠券，接口响应不合法：" + data);
                        reject("美团外卖优惠券：接口响应不合法");
                    }
                } catch (err) {
                    magicJS.logError("领取美团外卖优惠券，代码执行异常：" + err);
                    reject("美团外卖优惠券：代码执行异常");
                }
            }
        });
    });
}

// ---------------- 签到任务，领取1G日流量包 ----------------

// 获取签到任务列表
function GetSigninTasks(cookie, mobile) {
    let options = {
        url: "https://act.10010.com/SigninApp/doTask/getTaskInfo",
        headers: {
            "Accept": "application/json, text/plain, */*",
            "Accept-Encoding": "gzip, deflate, br",
            "Accept-Language": "zh-cn",
            "Connection": "keep-alive",
            "Content-Type": "application/x-www-form-urlencoded",
            "Cookie": cookie,
            "Host": "act.10010.com",
            "Origin": "https://img.client.10010.com",
            "Referer": `https://img.client.10010.com/SigininApp/index.html?version=iphone_c@8.0200&desmobile=${mobile}`,
            "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 unicom{version:iphone_c@8.0200}{systemVersion:dis}{yw_code:}"
        },
        body: "",
    };
    return new Promise((resolve, reject) => {
        magicJS.post(options, (err, resp, data) => {
            if (err) {
                magicJS.logError("获取签到任务列表失败，http请求异常：" + err);
                reject();
            } else {
                magicJS.logDebug("获取签到任务列表，接口响应数据：" + data);
                let obj = JSON.parse(data);
                if (obj.status === "0000") {
                    magicJS.logInfo("获取签到任务成功");
                    resolve();
                } else if (obj.status === "9999") {
                    magicJS.logError(obj.msg);
                    reject(obj.msg);
                } else {
                    magicJS.logWarning(`获取签到任务失败，接口响应不合法：\n${data}`);
                    reject("接口响应不合法");
                }
            }
        });
    }).catch((err) => {
        magicJS.logError(err);
    });
}

// 完成观看视频任务
function FinishVideo(cookie, mobile) {
    let options = {
        url: "https://act.10010.com/SigninApp/doTask/finishVideo",
        headers: {
            "Accept": "application/json, text/plain, */*",
            "Accept-Encoding": "gzip, deflate, br",
            "Accept-Language": "zh-cn",
            "Connection": "keep-alive",
            "Content-Type": "application/x-www-form-urlencoded",
            "Cookie": cookie,
            "Host": "act.10010.com",
            "Origin": "https://img.client.10010.com",
            "Referer": `https://img.client.10010.com/SigininApp/index.html?version=iphone_c@8.0200&desmobile=${mobile}`,
            "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 unicom{version:iphone_c@8.0200}{systemVersion:dis}{yw_code:}"
        },
        body: {
            orderId: "3E28C3BFBEB825F5FCD2F01DF9DB5141",
            imei: "5d58683de1d5d3b383a2e228cff935028a2399195f343cfe122eab1ddf90ee17"
        }
    };
    return new Promise((resolve, reject) => {
        magicJS.post(options, (err, resp, data) => {
            if (err) {
                magicJS.logError("完成观看视频任务失败，http请求异常：" + err);
                reject();
            } else {
                magicJS.logDebug("观看视频任务，接口响应数据：" + data);
                let obj = JSON.parse(data);
                if (obj.status === "0000") {
                    resolve();
                }
                if (obj.status === "9999") {
                    magicJS.logWarning(`观看视频任务失败，异常信息：\n${obj.msg}`);
                    reject(obj.msg);
                } else {
                    magicJS.logWarning(`观看视频任务失败，接口响应不合法：\n${data}`);
                    reject("接口响应不合法");
                }
            }
        });
    });
}

// 领取奖励
function GetSigninTaskPirze(cookie, mobile) {
    let options = {
        url: "https://act.10010.com/SigninApp/doTask/getPrize",
        headers: {
            "Accept": "application/json, text/plain, */*",
            "Accept-Encoding": "gzip, deflate, br",
            "Accept-Language": "zh-cn",
            "Connection": "keep-alive",
            "Content-Type": "application/x-www-form-urlencoded",
            "Cookie": cookie,
            "Host": "act.10010.com",
            "Origin": "https://img.client.10010.com",
            "Referer": `https://img.client.10010.com/SigininApp/index.html?version=iphone_c@8.0200&desmobile=${mobile}`,
            "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 unicom{version:iphone_c@8.0200}{systemVersion:dis}{yw_code:}"
        },
        body: "",
    };
    return new Promise((resolve, reject) => {
        magicJS.post(options, (err, resp, data) => {
            if (err) {
                magicJS.logError("领取签到任务奖励失败，http请求异常：" + err);
                reject();
            } else {
                magicJS.logDebug("领取签到任务奖励，接口响应数据：\n" + data);
                let obj = JSON.parse(data);
                if (obj.status === "0000" && obj.data.returnStr.indexOf("网络拥堵") < 0) {
                    resolve(obj.data.returnStr);
                } else if (obj.status === "0000" && obj.data.returnStr.indexOf("网络拥堵") >= 0) {
                    magicJS.logDebug(`领取签到任务奖励失败，网络拥堵：\n${data}`);
                    reject("网络拥堵");
                } else {
                    magicJS.logWarning(`领取签到任务奖励失败，接口响应不合法：\n${data}`);
                    reject("接口响应不合法");
                }
            }
        });
    });
}

// ---------------- 新版旧版共4次每日抽奖 ----------------

// 获取抽奖次数
function GetLotteryCount(cookie, encryptMobile) {
    let options = {
        url: "http://m.client.10010.com/dailylottery/static/active/findActivityInfojifen?areaCode=031&groupByType=&mobile=",
        headers: {
            "Accept": "*/*",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-cn",
            "Connection": "close",
            "Cookie": cookie,
            "Host": "m.client.10010.com",
            "Origin": "https://m.client.10010.com",
            "Referer": `http://m.client.10010.com/dailylottery/static/integral/firstpage?encryptmobile=${encryptMobile}`,
            "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
            "X-Requested-With": "XMLHttpRequest",
            "savedata": "false",
        },
        body: "",
    };
    return new Promise((resolve) => {
        magicJS.get(options, (err, resp, data) => {
            if (err) {
                magicJS.logError("获取抽奖次数失败，http请求异常：" + err);
                resolve(0);
            } else {
                try {
                    let obj = JSON.parse(data);
                    if (obj.hasOwnProperty("acFrequency")) {
                        let lotteryCount = Number(obj["acFrequency"]["totalAcFreq"]);
                        magicJS.logInfo("获取抽奖次数：" + lotteryCount);
                        resolve(lotteryCount);
                    } else {
                        magicJS.logWarning("获取抽奖次数异常，接口响应不合法：" + data);
                        resolve(0);
                    }
                } catch (err) {
                    magicJS.logError(`获取抽奖次数异常，代码执行异常：${err}，接口响应：${data}`);
                    resolve(0);
                }
            }
        });
    });
}

// 新版获取抽奖次数
function GetLotteryCountNewVersion(cookie, encryptMobile, cityCode) {
    let options = {
        url: `http://m.client.10010.com/dailylottery/static/active/findActivityInfo?areaCode=${cityCode}&groupByType=&mobile=${encryptMobile}`,
        headers: {
            "Accept": "*/*",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-cn",
            "Connection": "close",
            "Cookie": cookie,
            "Host": "m.client.10010.com",
            "Origin": "https://m.client.10010.com",
            "Referer": "http://m.client.10010.com/dailylottery/static/integral/firstpage?encryptmobile=",
            "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
            "X-Requested-With": "XMLHttpRequest",
            "savedata": "false",
        },
        body: "",
    };
    return new Promise((resolve) => {
        magicJS.get(options, (err, resp, data) => {
            if (err) {
                magicJS.logError("获取新版抽奖次数失败，http请求异常：" + err);
                resolve(0);
            } else {
                let obj = JSON.parse(data);
                if (obj.hasOwnProperty("acFrequency")) {
                    let lotteryCount = Number(obj["acFrequency"]["totalAcFreq"]);
                    magicJS.logInfo("获取新版抽奖次数：" + lotteryCount);
                    resolve(lotteryCount);
                } else {
                    magicJS.logWarning("获取新版抽奖次数异常，接口响应不合法：" + data);
                    resolve(0);
                }
            }
        });
    });
}

// 单次免费抽奖
function DailyLottery(cookie, encryptMobile) {
    let options = {
        url: `http://m.client.10010.com/dailylottery/static/integral/choujiang?usernumberofjsp=${encryptMobile}`,
        headers: {
            "Accept": "application/json, text/javascript, */*; q=0.01",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-cn",
            "Connection": "close",
            "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
            "Cookie": cookie,
            "Host": "m.client.10010.com",
            "Origin": "https://m.client.10010.com",
            "Referer": `http://m.client.10010.com/dailylottery/static/integral/firstpage?encryptmobile=${encryptMobile}`,
            "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
            "X-Requested-With": "XMLHttpRequest",
            "savedata": "false",
        },
        body: "",
    };
    return new Promise((resolve) => {
        magicJS.post(options, (err, resp, data) => {
            if (err) {
                magicJS.logError("每日免费抽奖，http请求异常：" + err);
                resolve("请求异常");
            } else {
                magicJS.logDebug("每日免费抽奖，接口响应数据：" + data);
                let obj = JSON.parse(data);
                if (obj.hasOwnProperty("Rsptype") && obj["Rsptype"] === "6666") {
                    resolve("次数不足");
                } else if (obj.hasOwnProperty("Rsptype") && obj["Rsptype"] === "3333") {
                    resolve("请求无效");
                } else if (obj.hasOwnProperty("RspMsg")) {
                    resolve(obj["RspMsg"]);
                } else {
                    magicJS.logWarning("每日免费抽奖，接口响应不合法：" + data);
                    resolve("接口响应不合法");
                }
            }
        });
    });
}

// 新版单次免费抽奖
function DailyLotteryNewVersion(cookie, encryptMobile) {
    let options = {
        url: `https://m.client.10010.com/dailylottery/static/doubleball/choujiang?usernumberofjsp=${encryptMobile}`,
        headers: {
            "Accept": "application/json, text/javascript, */*; q=0.01",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-cn",
            "Connection": "close",
            "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
            "Cookie": cookie,
            "Host": "m.client.10010.com",
            "Origin": "https://m.client.10010.com",
            "Referer": `http://m.client.10010.com/dailylottery/static/integral/firstpage?encryptmobile=${encryptMobile}`,
            "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
            "X-Requested-With": "XMLHttpRequest",
            "savedata": "false",
        },
        body: "",
    };
    return new Promise((resolve) => {
        magicJS.post(options, (err, resp, data) => {
            if (err) {
                magicJS.logError("新版每日免费抽奖，http请求异常：" + err);
                resolve("请求异常");
            } else {
                magicJS.logDebug("新版每日免费抽奖，接口响应数据：" + data);
                let obj = JSON.parse(data);
                if (obj.hasOwnProperty("Rsptype") && obj["Rsptype"] === "6666") {
                    resolve("次数不足");
                } else if (obj.hasOwnProperty("Rsptype") && obj["Rsptype"] === "3333") {
                    resolve("请求无效");
                } else if (obj.hasOwnProperty("RspMsg")) {
                    resolve(obj["RspMsg"]);
                } else {
                    magicJS.logWarning("新版每日免费抽奖，接口响应不合法：" + data);
                    resolve("接口响应不合法");
                }
            }
        });
    });
}

// 批量免费抽奖
async function StartDailyLottery(cookie, encryptMobile) {
    let lotteryCount = await GetLotteryCount(cookie, encryptMobile);
    let lotteryList = "";
    if (lotteryCount > 0) {
        for (let i = 0; i < lotteryCount; i++) {
            // 开始抽奖
            magicJS.logInfo(`第${i + 1}次免费抽奖开始`);
            if (lotteryList) {
                lotteryList += "\n";
            }
            lotteryList += `第${i + 1}次抽奖：${await DailyLottery(cookie, encryptMobile)}`;
        }
    }
    return [lotteryCount, lotteryList];
}

// 批量新版免费抽奖
async function StartDailyLotteryNewVersion(cookie, encryptMobile, cityCode, lotteryCount) {
    let lotteryNewVersionCount = await GetLotteryCountNewVersion(cookie, encryptMobile, cityCode);
    let lotteryNewVersionList = "";
    if (lotteryNewVersionCount > 0) {
        for (let i = 0; i < lotteryNewVersionCount; i++) {
            // 开始抽奖
            magicJS.logInfo(`新版第${i + 1}次免费抽奖开始`);
            if (lotteryNewVersionList) {
                lotteryNewVersionList += "\n";
            }
            lotteryNewVersionList += `第${lotteryCount + i + 1}次抽奖：${await DailyLotteryNewVersion(cookie, encryptMobile)}`;
        }
    }
    return [lotteryNewVersionCount, lotteryNewVersionList];
}

(async () => {
    if (magicJS.isRequest) {
        // 从天天抽奖中获取Cookie等四件套信息
        if (getLotteryCookieRegex.test(magicJS.request.url)) {
            try {
                // 获取cookie
                let cookie = magicJS.request.headers["Cookie"];
                let hisCookie = magicJS.read(unicomCookieKey);
                // 多种方法获取手机号
                let mobile01 = /c_mobile=(\d{11})/.exec(cookie);
                let mobile02 = /mobileServiceAll=(\d{11})/.exec(cookie);
                let mobile03 = /u_account=(\d{11})/.exec(cookie);
                let mobile04 = /desmobile==(\d{11})/.exec(magicJS.request.headers["Referer"]);
                let mobile = "";
                if (!!mobile01) {
                    mobile = mobile01[1];
                } else if (!!mobile02) {
                    mobile = mobile02[1];
                } else if (!!mobile03) {
                    mobile = mobile03[1];
                } else {
                    mobile = mobile04[1];
                }
                let hisMobile = magicJS.read(mobileKey);
                // 获取加密手机号
                let encryptMobile = /encryptmobile=([a-zA-Z0-9]*)/.exec(magicJS.request.url)[1];
                let hisEncryptMobile = magicJS.read(encryptMobileKey);
                let cityCode = /city=(\d*)/.exec(magicJS.request.headers["Cookie"])[1];
                // 获取城市代码
                let hisCityCode = magicJS.read(cityCodeKey);
                let notifyContent = "";
                magicJS.logInfo(`新的cookie：${cookie}\n\n旧的cookie：${hisCookie}`);
                magicJS.logInfo(`新的手机号：${mobile}\n旧的手机号：${hisMobile}`);
                magicJS.logInfo(`新的手机号密文：${encryptMobile}\n旧的手机号密文：${hisEncryptMobile}`);
                magicJS.logInfo(`新的城市代码：${cityCode}\n旧的城市代码：${hisCityCode}`);
                // cookie
                if (cookie !== hisCookie) {
                    magicJS.write(unicomCookieKey, cookie);
                    if (!hisCookie) {
                        magicJS.logInfo("首次获取联通cookie成功：" + cookie);
                        notifyContent += "🍩联通cookie:获取成功";
                    } else {
                        magicJS.logInfo("更新联通cookie成功：" + cookie);
                        notifyContent += "🍩联通cookie:更新成功";
                    }
                } else {
                    magicJS.logInfo("联通cookie没有变化，无需更新");
                    notifyContent += "🍩联通cookie:没有变化";
                }
                // 手机号
                if (mobile !== hisMobile) {
                    magicJS.write(mobileKey, mobile);
                    if (!hisMobile) {
                        notifyContent += " 📱手机号:获取成功";
                    } else {
                        notifyContent += " 📱手机号:更新成功";
                    }
                } else {
                    magicJS.logInfo("手机号码没有变化，无需更新");
                    notifyContent += " 📱手机号:没有变化";
                }
                // 手机号密文
                if (hisEncryptMobile !== encryptMobile) {
                    magicJS.write(encryptMobileKey, encryptMobile);
                    if (!hisEncryptMobile) {
                        notifyContent += "\n🗳手机号密文:获取成功";
                    } else {
                        notifyContent += "\n🗳手机号密文:更新成功";
                    }
                } else {
                    magicJS.logInfo("手机号码密文没有变化，无需更新");
                    notifyContent += "\n🗳手机号密文:没有变化";
                }
                if (cityCode !== hisCityCode) {
                    magicJS.write(cityCodeKey, cityCode);
                    if (!hisCityCode) {
                        magicJS.logInfo("首次获取联通城市代码成功：" + cityCode);
                        notifyContent += " 🌃城市:获取成功";
                    } else {
                        magicJS.logInfo("更新联通城市代码成功：" + cityCode);
                        notifyContent += " 🌃城市:更新成功";
                    }
                } else {
                    magicJS.logInfo("城市代码没有变化，无需更新");
                    notifyContent += " 🌃城市:没有变化";
                }
                magicJS.notify(scriptName, "", notifyContent);
            } catch (err) {
                magicJS.logError(`获取联通手机营业厅Cookie出现异常，异常信息：${err}`);
            }
        }
    } else {
        magicJS.logInfo("签到与抽奖开始执行！");
        let cookie = magicJS.read(unicomCookieKey);
        let mobile = magicJS.read(mobileKey);
        let encryptMobile = magicJS.read(encryptMobileKey);
        let cityCode = magicJS.read(cityCodeKey);
        if (!!!cookie) {
            magicJS.logError("没有获取到联通手机营业厅Cookie，请在App中搜索天天抽奖获取。");
            magicJS.notify("❌没有获取到Cookie\n请在手机营业厅中搜索“天天抽奖”获取");
        } else if (cookie.hasOwnProperty("default") || cookie.indexOf("default") >= 0) {
            magicJS.notify("❌新版签到需要重新获取Cookie\n请在App中搜索“天天抽奖”获取");
            magicJS.read(unicomCookieKey, "");
            magicJS.read(mobileKey, "");
            magicJS.read(encryptMobileKey, "");
            magicJS.read(cityCodeKey, "");
        } else {
            // 生成签到结果的通知
            let notifyTitle = scriptName;
            let notifySubTtile = "";
            let notifyContent = "";

            // 用户登录
            let [errUserLogin, [loginResult, loginStr] = [false, "用户登录失败"]] = await magicJS.attempt(magicJS.retry(UserLogin, 5, 1000)(cookie, encryptMobile));
            if (errUserLogin || loginResult === false) {
                notifySubTtile = "❌用户登录出现异常，请查阅日志！";
            } else {
                // 用户签到，如失败重试10次
                let AppSigninPromise = magicJS.retry(AppSignin, 10, 100)(cookie);
                let [, [signinResult, siginiResultStr, prizeCount, growthV, flowerCount] = [false, "签到异常", null, null, null]] = await magicJS.attempt(AppSigninPromise);
                if (signinResult === true) {
                    // let [, doublePrizeCount] = await magicJS.attempt(magicJS.retry(DoubleAdPlaying, 5, 200)(cookie, mobile));
                    let doublePrizeCount = null;
                    notifySubTtile = siginiResultStr;
                    if (doublePrizeCount) prizeCount += doublePrizeCount;
                    if (prizeCount) notifyContent += `积分+${prizeCount} `;
                    if (growthV) notifyContent += `成长值+${growthV} `;
                    if (flowerCount) notifyContent += `鲜花+${flowerCount} `;
                }

                // 查询连续签到天数
                let genContinueCountPromise = magicJS.retry(GetContinueCount, 10, 100)(cookie);
                let [, contineCount] = await magicJS.attempt(genContinueCountPromise);
                if (contineCount) {
                    notifySubTtile += ` 连续签到${contineCount}天`;
                }

                // 查询用户信息
                let getUserInfoPromise = magicJS.retry(GetUserInfo, 10, 100)(cookie, mobile);
                let [, userInfo] = await magicJS.attempt(getUserInfoPromise);
                if (userInfo && userInfo.hasOwnProperty("flow") && userInfo.hasOwnProperty("fee")) {
                    let userInfoStr = `${userInfo["flow"]} ${userInfo["fee"]}\n${userInfo["voice"]} ${userInfo["point"]}`;
                    notifyContent += notifyContent ? `\n${userInfoStr}` : userInfoStr;
                }

                // 领取美团外卖优惠券
                let getMeituanCouponPromise = magicJS.retry(GetMeituanCoupon, 3, 100)(cookie);
                let [, meituanResult = null] = await magicJS.attempt(getMeituanCouponPromise);
                if (meituanResult) {
                    notifyContent += notifyContent ? `\n${meituanResult}` : meituanResult;
                }

                // 完成签到任务，领取日流量包
                // if (signinResult === true) {
                //   await magicJS
                //     .retry(
                //       FinishVideo,
                //       10,
                //       500
                //     )(cookie, mobile)
                //     .catch((err) => magicJS.logError(`完成观看视频任务失败，异常信息：${err}`));
                //   await magicJS
                //     .retry(
                //       GetSigninTasks,
                //       10,
                //       500
                //     )(cookie, mobile)
                //     .catch((err) => magicJS.logError(`获取签到任务失败，异常信息：${err}`));
                //   await magicJS
                //     .retry(
                //       GetSigninTaskPirze,
                //       10,
                //       500
                //     )(cookie, mobile)
                //     .then((getPrizeStr) => {
                //         notifyContent += notifyContent ? `\n${getPrizeStr}` : getPrizeStr;
                //     })
                //     .catch((err) => magicJS.logError(`领取任务奖励失败，异常信息：${err}`));
                // }

                // 旧版抽奖
                let [errLottery, [lotteryCount, lotteryResult] = []] = await magicJS.attempt(StartDailyLottery(cookie, encryptMobile));
                if (errLottery) magicJS.logError("旧版抽奖出现异常：" + errLottery);
                // 新版抽奖
                let [errLotteryNewVersion, [lotteryNewVersionCount, lotteryNewVersionResult] = []] = await magicJS.attempt(StartDailyLotteryNewVersion(cookie, encryptMobile, cityCode, lotteryCount));
                if (errLotteryNewVersion) magicJS.logError("新版抽奖出现异常：" + errLotteryNewVersion);
                if (lotteryResult) {
                    notifyContent += notifyContent ? `\n${lotteryResult}` : lotteryResult;
                }
                if (lotteryNewVersionResult) {
                    notifyContent += notifyContent ? `\n${lotteryNewVersionResult}` : lotteryNewVersionResult;
                }
            }

            // 通知与结束脚本
            magicJS.logInfo("签到与抽奖执行完毕！");
            magicJS.notify(notifyTitle, notifySubTtile, notifyContent);
            magicJS.done();
        }
    }
    magicJS.done();
})();

function MagicJS(scriptName = "MagicJS", logLevel = "INFO") {
    return new (class {
        constructor() {
            this.version = "2.2.3.2";
            this.scriptName = scriptName;
            this.logLevels = {DEBUG: 5, INFO: 4, NOTIFY: 3, WARNING: 2, ERROR: 1, CRITICAL: 0, NONE: -1};
            this.isLoon = typeof $loon !== "undefined";
            this.isQuanX = typeof $task !== "undefined";
            this.isJSBox = typeof $drive !== "undefined";
            this.isNode = typeof module !== "undefined" && !this.isJSBox;
            this.isSurge = typeof $httpClient !== "undefined" && !this.isLoon;
            this.node = {request: undefined, fs: undefined, data: {}};
            this.iOSUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Mobile/15E148 Safari/604.1";
            this.pcUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36 Edg/84.0.522.59";
            this.logLevel = logLevel;
            this._barkUrl = "";
            if (this.isNode) {
                this.node.fs = require("fs");
                this.node.request = require("request");
                try {
                    this.node.fs.accessSync("./magic.json", this.node.fs.constants.R_OK | this.node.fs.constants.W_OK);
                } catch (err) {
                    this.node.fs.writeFileSync("./magic.json", "{}", {encoding: "utf8"});
                }
                this.node.data = require("./magic.json");
            } else if (this.isJSBox) {
                if (!$file.exists("drive://MagicJS")) {
                    $file.mkdir("drive://MagicJS");
                }
                if (!$file.exists("drive://MagicJS/magic.json")) {
                    $file.write({
                        data: $data({string: "{}"}),
                        path: "drive://MagicJS/magic.json",
                    });
                }
            }
        }

        /**
         * @param {string} url
         */
        set barkUrl(url) {
            this._barkUrl = url.replace(/\/+$/g, "");
        }

        set logLevel(level) {
            this._logLevel = typeof level === "string" ? level.toUpperCase() : "DEBUG";
        }

        get logLevel() {
            return this._logLevel;
        }

        get isRequest() {
            return typeof $request !== "undefined" && typeof $response === "undefined";
        }

        get isResponse() {
            return typeof $response !== "undefined";
        }

        get request() {
            return typeof $request !== "undefined" ? $request : undefined;
        }

        get response() {
            if (typeof $response !== "undefined") {
                if ($response.hasOwnProperty("status")) $response["statusCode"] = $response["status"];
                if ($response.hasOwnProperty("statusCode")) $response["status"] = $response["statusCode"];
                return $response;
            } else {
                return undefined;
            }
        }

        get platform() {
            if (this.isSurge) return "Surge";
            else if (this.isQuanX) return "Quantumult X";
            else if (this.isLoon) return "Loon";
            else if (this.isJSBox) return "JSBox";
            else if (this.isNode) return "Node.js";
            else return "unknown";
        }

        read(key, session = "") {
            let val = "";
            // 读取原始数据
            if (this.isSurge || this.isLoon) {
                val = $persistentStore.read(key);
            } else if (this.isQuanX) {
                val = $prefs.valueForKey(key);
            } else if (this.isNode) {
                val = this.node.data;
            } else if (this.isJSBox) {
                val = $file.read("drive://MagicJS/magic.json").string;
            }
            try {
                // Node 和 JSBox数据处理
                if (this.isNode) val = val[key];
                if (this.isJSBox) val = JSON.parse(val)[key];
                // 带Session的情况
                if (!!session) {
                    if (typeof val === "string") val = JSON.parse(val);
                    val = !!val && typeof val === "object" ? val[session] : null;
                }
            } catch (err) {
                this.logError(err);
                val = !!session ? {} : null;
                this.del(key);
            }
            if (typeof val === "undefined") val = null;
            try {
                if (!!val && typeof val === "string") val = JSON.parse(val);
            } catch (err) {
            }
            this.logDebug(`READ DATA [${key}]${!!session ? `[${session}]` : ""}(${typeof val})\n${JSON.stringify(val)}`);
            return val;
        }

        write(key, val, session = "") {
            let data = !!session ? {} : "";
            // 读取原先存储的JSON格式数据
            if (!!session && (this.isSurge || this.isLoon)) {
                data = $persistentStore.read(key);
            } else if (!!session && this.isQuanX) {
                data = $prefs.valueForKey(key);
            } else if (this.isNode) {
                data = this.node.data;
            } else if (this.isJSBox) {
                data = JSON.parse($file.read("drive://MagicJS/magic.json").string);
            }
            if (!!session) {
                // 有Session，所有数据都是Object
                try {
                    if (typeof data === "string") data = JSON.parse(data);
                    data = typeof data === "object" && !!data ? data : {};
                } catch (err) {
                    this.logError(err);
                    this.del(key);
                    data = {};
                }
                if (this.isJSBox || this.isNode) {
                    // 构造数据
                    if (!data.hasOwnProperty(key) || typeof data[key] != "object") {
                        data[key] = {};
                    }
                    if (!data[key].hasOwnProperty(session)) {
                        data[key][session] = null;
                    }
                    // 写入或删除数据
                    if (typeof val === "undefined") {
                        delete data[key][session];
                    } else {
                        data[key][session] = val;
                    }
                } else {
                    // 写入或删除数据
                    if (typeof val === "undefined") {
                        delete data[session];
                    } else {
                        data[session] = val;
                    }
                }
            }
            // 没有Session时
            else {
                if (this.isNode || this.isJSBox) {
                    // 删除数据
                    if (typeof val === "undefined") {
                        delete data[key];
                    } else {
                        data[key] = val;
                    }
                } else {
                    // 删除数据
                    if (typeof val === "undefined") {
                        data = null;
                    } else {
                        data = val;
                    }
                }
            }
            // 数据回写
            if (typeof data === "object") data = JSON.stringify(data);
            if (this.isSurge || this.isLoon) {
                $persistentStore.write(data, key);
            } else if (this.isQuanX) {
                $prefs.setValueForKey(data, key);
            } else if (this.isNode) {
                this.node.fs.writeFileSync("./magic.json", data);
            } else if (this.isJSBox) {
                $file.write({data: $data({string: data}), path: "drive://MagicJS/magic.json"});
            }
            this.logDebug(`WRITE DATA [${key}]${!!session ? `[${session}]` : ""}(${typeof val})\n${JSON.stringify(val)}`);
        }

        del(key, session = "") {
            this.logDebug(`DELETE KEY [${key}]${!!session ? `[${session}]` : ""}`);
            this.write(key, null, session);
        }

        /**
         * iOS系统通知
         * @param {*} title 通知标题
         * @param {*} subTitle 通知副标题
         * @param {*} body 通知内容
         * @param {*} opts 通知选项，目前支持传入超链接或Object
         * Surge不支持通知选项，Loon和QuantumultX支持打开URL和多媒体通知
         * opts "applestore://" 打开Apple Store
         * opts "https://www.apple.com.cn/" 打开Apple.com.cn
         * opts {'open-url': 'https://www.apple.com.cn/'} 打开Apple.com.cn
         * opts {'open-url': 'https://www.apple.com.cn/', 'media-url': 'https://raw.githubusercontent.com/Orz-3/mini/master/Apple.png'} 打开Apple.com.cn，显示一个苹果Logo
         */
        notify(title = this.scriptName, subTitle = "", body = "", opts = "") {
            let convertOptions = (_opts) => {
                let newOpts = {};
                if (typeof _opts === "string") {
                    if (this.isLoon) newOpts = {openUrl: _opts};
                    else if (this.isQuanX) newOpts = {"open-url": _opts};
                } else if (typeof _opts === "object") {
                    if (this.isLoon) {
                        newOpts["openUrl"] = !!_opts["open-url"] ? _opts["open-url"] : "";
                        newOpts["mediaUrl"] = !!_opts["media-url"] ? _opts["media-url"] : "";
                    } else if (this.isQuanX) newOpts = !!_opts["open-url"] || !!_opts["media-url"] ? _opts : {};
                }
                return newOpts;
            };
            opts = convertOptions(opts);
            // 支持单个参数通知
            if (arguments.length == 1) {
                title = this.scriptName;
                (subTitle = ""), (body = arguments[0]);
            }
            // 生成通知日志
            this.logNotify(`title:${title}\nsubTitle:${subTitle}\nbody:${body}\noptions:${typeof opts === "object" ? JSON.stringify(opts) : opts}`);
            if (this.isSurge) {
                $notification.post(title, subTitle, body);
            } else if (this.isLoon) {
                if (!!opts) $notification.post(title, subTitle, body, opts);
                else $notification.post(title, subTitle, body);
            } else if (this.isQuanX) {
                $notify(title, subTitle, body, opts);
            } else if (this.isNode) {
                if (!!this._barkUrl) {
                    let content = encodeURI(`${title}/${subTitle}\n${body}`);
                    this.get(`${this._barkUrl}/${content}`, () => {
                    });
                }
            } else if (this.isJSBox) {
                let push = {
                    title: title,
                    body: !!subTitle ? `${subTitle}\n${body}` : body,
                };
                $push.schedule(push);
            }
        }

        log(msg, level = "INFO") {
            if (!(this.logLevels[this._logLevel] < this.logLevels[level.toUpperCase()])) console.log(`[${level}] [${this.scriptName}]\n${msg}\n`);
        }

        logDebug(msg) {
            this.log(msg, "DEBUG");
        }

        logInfo(msg) {
            this.log(msg, "INFO");
        }

        logNotify(msg) {
            this.log(msg, "NOTIFY");
        }

        logWarning(msg) {
            this.log(msg, "WARNING");
        }

        logError(msg) {
            this.log(msg, "ERROR");
        }

        logRetry(msg) {
            this.log(msg, "RETRY");
        }

        /**
         * 对传入的Http Options根据不同环境进行适配
         * @param {*} options
         */
        adapterHttpOptions(options, method) {
            let _options = typeof options === "object" ? Object.assign({}, options) : {url: options, headers: {}};

            if (_options.hasOwnProperty("header") && !_options.hasOwnProperty("headers")) {
                _options["headers"] = _options["header"];
                delete _options["header"];
            }

            // 规范化的headers
            const headersMap = {
                "accept": "Accept",
                "accept-ch": "Accept-CH",
                "accept-charset": "Accept-Charset",
                "accept-features": "Accept-Features",
                "accept-encoding": "Accept-Encoding",
                "accept-language": "Accept-Language",
                "accept-ranges": "Accept-Ranges",
                "access-control-allow-credentials": "Access-Control-Allow-Credentials",
                "access-control-allow-origin": "Access-Control-Allow-Origin",
                "access-control-allow-methods": "Access-Control-Allow-Methods",
                "access-control-allow-headers": "Access-Control-Allow-Headers",
                "access-control-max-age": "Access-Control-Max-Age",
                "access-control-expose-headers": "Access-Control-Expose-Headers",
                "access-control-request-method": "Access-Control-Request-Method",
                "access-control-request-headers": "Access-Control-Request-Headers",
                "age": "Age",
                "allow": "Allow",
                "alternates": "Alternates",
                "authorization": "Authorization",
                "cache-control": "Cache-Control",
                "connection": "Connection",
                "content-encoding": "Content-Encoding",
                "content-language": "Content-Language",
                "content-length": "Content-Length",
                "content-location": "Content-Location",
                "content-md5": "Content-MD5",
                "content-range": "Content-Range",
                "content-security-policy": "Content-Security-Policy",
                "content-type": "Content-Type",
                "cookie": "Cookie",
                "dnt": "DNT",
                "date": "Date",
                "etag": "ETag",
                "expect": "Expect",
                "expires": "Expires",
                "from": "From",
                "host": "Host",
                "if-match": "If-Match",
                "if-modified-since": "If-Modified-Since",
                "if-none-match": "If-None-Match",
                "if-range": "If-Range",
                "if-unmodified-since": "If-Unmodified-Since",
                "last-event-id": "Last-Event-ID",
                "last-modified": "Last-Modified",
                "link": "Link",
                "location": "Location",
                "max-forwards": "Max-Forwards",
                "negotiate": "Negotiate",
                "origin": "Origin",
                "pragma": "Pragma",
                "proxy-authenticate": "Proxy-Authenticate",
                "proxy-authorization": "Proxy-Authorization",
                "range": "Range",
                "referer": "Referer",
                "retry-after": "Retry-After",
                "sec-websocket-extensions": "Sec-Websocket-Extensions",
                "sec-websocket-key": "Sec-Websocket-Key",
                "sec-websocket-origin": "Sec-Websocket-Origin",
                "sec-websocket-protocol": "Sec-Websocket-Protocol",
                "sec-websocket-version": "Sec-Websocket-Version",
                "server": "Server",
                "set-cookie": "Set-Cookie",
                "set-cookie2": "Set-Cookie2",
                "strict-transport-security": "Strict-Transport-Security",
                "tcn": "TCN",
                "te": "TE",
                "trailer": "Trailer",
                "transfer-encoding": "Transfer-Encoding",
                "upgrade": "Upgrade",
                "user-agent": "User-Agent",
                "variant-vary": "Variant-Vary",
                "vary": "Vary",
                "via": "Via",
                "warning": "Warning",
                "www-authenticate": "WWW-Authenticate",
                "x-content-duration": "X-Content-Duration",
                "x-content-security-policy": "X-Content-Security-Policy",
                "x-dnsprefetch-control": "X-DNSPrefetch-Control",
                "x-frame-options": "X-Frame-Options",
                "x-requested-with": "X-Requested-With",
                "x-surge-skip-scripting": "X-Surge-Skip-Scripting",
            };
            if (typeof _options.headers === "object") {
                for (let key in _options.headers) {
                    if (headersMap[key]) {
                        _options.headers[headersMap[key]] = _options.headers[key];
                        delete _options.headers[key];
                    }
                }
            }

            // 自动补完User-Agent，减少请求特征
            if (!!!_options.headers || typeof _options.headers !== "object" || !!!_options.headers["User-Agent"]) {
                if (!!!_options.headers || typeof _options.headers !== "object") _options.headers = {};
                if (this.isNode) _options.headers["User-Agent"] = this.pcUserAgent;
                else _options.headers["User-Agent"] = this.iOSUserAgent;
            }

            // 判断是否跳过脚本处理
            let skipScripting = false;
            if ((typeof _options["opts"] === "object" && (_options["opts"]["hints"] === true || _options["opts"]["Skip-Scripting"] === true)) || (typeof _options["headers"] === "object" && _options["headers"]["X-Surge-Skip-Scripting"] === true)) {
                skipScripting = true;
            }
            if (!skipScripting) {
                if (this.isSurge) _options.headers["X-Surge-Skip-Scripting"] = false;
                else if (this.isLoon) _options.headers["X-Requested-With"] = "XMLHttpRequest";
                else if (this.isQuanX) {
                    if (typeof _options["opts"] !== "object") _options.opts = {};
                    _options.opts["hints"] = false;
                }
            }

            // 对请求数据做清理
            if (!this.isSurge || skipScripting) delete _options.headers["X-Surge-Skip-Scripting"];
            if (!this.isQuanX && _options.hasOwnProperty("opts")) delete _options["opts"];
            if (this.isQuanX && _options.hasOwnProperty("opts")) delete _options["opts"]["Skip-Scripting"];

            // GET请求将body转换成QueryString(beta)
            if (method === "GET" && !this.isNode && !!_options.body) {
                let qs = Object.keys(_options.body)
                    .map((key) => {
                        if (typeof _options.body === "undefined") return "";
                        return `${encodeURIComponent(key)}=${encodeURIComponent(_options.body[key])}`;
                    })
                    .join("&");
                if (_options.url.indexOf("?") < 0) _options.url += "?";
                if (_options.url.lastIndexOf("&") + 1 != _options.url.length && _options.url.lastIndexOf("?") + 1 != _options.url.length) _options.url += "&";
                _options.url += qs;
                delete _options.body;
            }

            // 适配多环境
            if (this.isQuanX) {
                if (_options.hasOwnProperty("body") && typeof _options["body"] !== "string") _options["body"] = JSON.stringify(_options["body"]);
                _options["method"] = method;
            } else if (this.isNode) {
                delete _options.headers["Accept-Encoding"];
                if (typeof _options.body === "object") {
                    if (method === "GET") {
                        _options.qs = _options.body;
                        delete _options.body;
                    } else if (method === "POST") {
                        _options["json"] = true;
                        _options.body = _options.body;
                    }
                }
            } else if (this.isJSBox) {
                _options["header"] = _options["headers"];
                delete _options["headers"];
            }

            return _options;
        }

        /**
         * Http客户端发起GET请求
         * @param {*} options
         * @param {*} callback
         * options可配置参数headers和opts，用于判断由脚本发起的http请求是否跳过脚本处理。
         * 支持Surge和Quantumult X两种配置方式。
         * 以下几种配置会跳过脚本处理，options没有opts或opts的值不匹配，则不跳过脚本处理
         * {opts:{"hints": true}}
         * {opts:{"Skip-Scripting": true}}
         * {headers: {"X-Surge-Skip-Scripting": true}}
         */
        get(options, callback) {
            let _options = this.adapterHttpOptions(options, "GET");
            this.logDebug(`HTTP GET: ${JSON.stringify(_options)}`);
            if (this.isSurge || this.isLoon) {
                $httpClient.get(_options, callback);
            } else if (this.isQuanX) {
                $task.fetch(_options).then(
                    (resp) => {
                        resp["status"] = resp.statusCode;
                        callback(null, resp, resp.body);
                    },
                    (reason) => callback(reason.error, null, null)
                );
            } else if (this.isNode) {
                return this.node.request.get(_options, callback);
            } else if (this.isJSBox) {
                _options["handler"] = (resp) => {
                    let err = resp.error ? JSON.stringify(resp.error) : undefined;
                    let data = typeof resp.data === "object" ? JSON.stringify(resp.data) : resp.data;
                    callback(err, resp.response, data);
                };
                $http.get(_options);
            }
        }

        /**
         * Http客户端发起POST请求
         * @param {*} options
         * @param {*} callback
         * options可配置参数headers和opts，用于判断由脚本发起的http请求是否跳过脚本处理。
         * 支持Surge和Quantumult X两种配置方式。
         * 以下几种配置会跳过脚本处理，options没有opts或opts的值不匹配，则不跳过脚本处理
         * {opts:{"hints": true}}
         * {opts:{"Skip-Scripting": true}}
         * {headers: {"X-Surge-Skip-Scripting": true}}
         */
        post(options, callback) {
            let _options = this.adapterHttpOptions(options, "POST");
            this.logDebug(`HTTP POST: ${JSON.stringify(_options)}`);
            if (this.isSurge || this.isLoon) {
                $httpClient.post(_options, callback);
            } else if (this.isQuanX) {
                $task.fetch(_options).then(
                    (resp) => {
                        resp["status"] = resp.statusCode;
                        callback(null, resp, resp.body);
                    },
                    (reason) => {
                        callback(reason.error, null, null);
                    }
                );
            } else if (this.isNode) {
                return this.node.request.post(_options, callback);
            } else if (this.isJSBox) {
                _options["handler"] = (resp) => {
                    let err = resp.error ? JSON.stringify(resp.error) : undefined;
                    let data = typeof resp.data === "object" ? JSON.stringify(resp.data) : resp.data;
                    callback(err, resp.response, data);
                };
                $http.post(_options);
            }
        }

        done(value = {}) {
            if (typeof $done !== "undefined") {
                $done(value);
            }
        }

        isToday(day) {
            if (day == null) {
                return false;
            } else {
                let today = new Date();
                if (typeof day == "string") {
                    day = new Date(day);
                }
                if (today.getFullYear() == day.getFullYear() && today.getMonth() == day.getMonth() && today.getDay() == day.getDay()) {
                    return true;
                } else {
                    return false;
                }
            }
        }

        isNumber(val) {
            return parseFloat(val).toString() === "NaN" ? false : true;
        }

        /**
         * 对await执行中出现的异常进行捕获并返回，避免写过多的try catch语句
         * 示例：let [err,val] = await magicJS.attempt(func(), 'defaultvalue');
         * 或者：let [err, [val1,val2]] = await magicJS.attempt(func(), ['defaultvalue1', 'defaultvalue2']);
         * @param {*} promise Promise 对象
         * @param {*} defaultValue 出现异常时返回的默认值
         * @returns 返回两个值，第一个值为异常，第二个值为执行结果
         */
        attempt(promise, defaultValue = null) {
            return promise
                .then((args) => {
                    return [null, args];
                })
                .catch((ex) => {
                    this.logError(ex);
                    return [ex, defaultValue];
                });
        }

        /**
         * 重试方法
         * @param {*} fn 需要重试的函数
         * @param {number} [retries=5] 重试次数
         * @param {number} [interval=0] 每次重试间隔
         * @param {function} [callback=null] 函数没有异常时的回调，会将函数执行结果result传入callback，根据result的值进行判断，如果需要再次重试，在callback中throw一个异常，适用于函数本身没有异常但仍需重试的情况。
         * @returns 返回一个Promise对象
         */
        retry(fn, retries = 5, interval = 0, callback = null) {
            return (...args) => {
                return new Promise((resolve, reject) => {
                    function _retry(...args) {
                        Promise.resolve()
                            .then(() => fn.apply(this, args))
                            .then((result) => {
                                if (typeof callback === "function") {
                                    Promise.resolve()
                                        .then(() => callback(result))
                                        .then(() => {
                                            resolve(result);
                                        })
                                        .catch((ex) => {
                                            if (retries >= 1) {
                                                if (interval > 0) setTimeout(() => _retry.apply(this, args), interval);
                                                else _retry.apply(this, args);
                                            } else {
                                                reject(ex);
                                            }
                                            retries--;
                                        });
                                } else {
                                    resolve(result);
                                }
                            })
                            .catch((ex) => {
                                this.logRetry(ex);
                                if (retries >= 1 && interval > 0) {
                                    setTimeout(() => _retry.apply(this, args), interval);
                                } else if (retries >= 1) {
                                    _retry.apply(this, args);
                                } else {
                                    reject(ex);
                                }
                                retries--;
                            });
                    }

                    _retry.apply(this, args);
                });
            };
        }

        formatTime(time, fmt = "yyyy-MM-dd hh:mm:ss") {
            var o = {
                "M+": time.getMonth() + 1,
                "d+": time.getDate(),
                "h+": time.getHours(),
                "m+": time.getMinutes(),
                "s+": time.getSeconds(),
                "q+": Math.floor((time.getMonth() + 3) / 3),
                "S": time.getMilliseconds(),
            };
            if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (time.getFullYear() + "").substr(4 - RegExp.$1.length));
            for (let k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
            return fmt;
        }

        now() {
            return this.formatTime(new Date(), "yyyy-MM-dd hh:mm:ss");
        }

        today() {
            return this.formatTime(new Date(), "yyyy-MM-dd");
        }

        sleep(time) {
            return new Promise((resolve) => setTimeout(resolve, time));
        }
    })(scriptName);
}
